﻿using System;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;

namespace Esponce.QRCode.Content
{
	/// <summary>
	/// Parses and builds a VCARD format.
	/// </summary>
	/// <remarks>
	/// References:
	/// http://en.wikipedia.org/wiki/VCard
	/// http://tools.ietf.org/html/rfc4770
	/// 
	/// BEGIN:VCARD
	/// VERSION:2.1
	/// N:Esponce;Monkey
	/// FN:Monkey Esponce
	/// ...
	/// END:VCARD
	/// </remarks>
	public class VCardContentFormat : ContentFormatBase<VCardContentFormat>, IContactContentFormat
	{
		#region Public properties
		public string Identifier
		{
			get
			{
				return this.identifier;
			}
			set
			{
				if (this.identifier != value)
				{
					this.identifier = value;
					OnPropertyChanged("Identifier");
				}
			}
		}
		private string identifier;

		public string JobTitle
		{
			get
			{
				return this.jobTitle;
			}
			set
			{
				if (this.jobTitle != value)
				{
					this.jobTitle = value;
					OnPropertyChanged("JobTitle");
				}
			}
		}
		private string jobTitle;

		public string Role
		{
			get
			{
				return this.role;
			}
			set
			{
				if (this.role != value)
				{
					this.role = value;
					OnPropertyChanged("Role");
				}
			}
		}
		private string role;

		public string FirstName
		{
			get
			{
				return this.firstName;
			}
			set
			{
				if (this.firstName != value)
				{
					this.firstName = value;
					OnPropertyChanged("FirstName");
				}
			}
		}
		private string firstName;

		public string LastName
		{
			get
			{
				return this.lastName;
			}
			set
			{
				if (this.lastName != value)
				{
					this.lastName = value;
					OnPropertyChanged("LastName");
				}
			}
		}
		private string lastName;

		public string CompanyName
		{
			get
			{
				return this.companyName;
			}
			set
			{
				if (this.companyName != value)
				{
					this.companyName = value;
					OnPropertyChanged("CompanyName");
				}
			}
		}
		private string companyName;

		public string Address
		{
			get
			{
				return this.address;
			}
			set
			{
				if (this.address != value)
				{
					this.address = value;
					OnPropertyChanged("Address");
				}
			}
		}
		private string address;

		public string City
		{
			get
			{
				return this.city;
			}
			set
			{
				if (this.city != value)
				{
					this.city = value;
					OnPropertyChanged("City");
				}
			}
		}
		private string city;

		public string ZipCode
		{
			get
			{
				return this.zipCode;
			}
			set
			{
				if (this.zipCode != value)
				{
					this.zipCode = value;
					OnPropertyChanged("ZipCode");
				}
			}
		}
		private string zipCode;

		public string State
		{
			get
			{
				return this.state;
			}
			set
			{
				if (this.state != value)
				{
					this.state = value;
					OnPropertyChanged("State");
				}
			}
		}
		private string state;

		public string Country
		{
			get
			{
				return this.country;
			}
			set
			{
				if (this.country != value)
				{
					this.country = value;
					OnPropertyChanged("Country");
				}
			}
		}
		private string country;

		public DateTime? Birthday
		{
			get
			{
				return this.birthday;
			}
			set
			{
				if (this.birthday != value)
				{
					this.birthday = value;
					OnPropertyChanged("Birthday");
				}
			}
		}
		private DateTime? birthday;

		public string PhoneNumber
		{
			get
			{
				return this.phoneNumber;
			}
			set
			{
				if (this.phoneNumber != value)
				{
					this.phoneNumber = value;
					OnPropertyChanged("PhoneNumber");
				}
			}
		}
		private string phoneNumber;

		public string MobileNumber
		{
			get
			{
				return this.mobileNumber;
			}
			set
			{
				if (this.mobileNumber != value)
				{
					this.mobileNumber = value;
					OnPropertyChanged("MobileNumber");
				}
			}
		}
		private string mobileNumber;

		public string EmailAddress
		{
			get
			{
				return this.emailAddress;
			}
			set
			{
				if (this.emailAddress != value)
				{
					this.emailAddress = value;
					OnPropertyChanged("EmailAddress");
				}
			}
		}
		private string emailAddress;

		public string WebsiteUrl
		{
			get
			{
				return this.websiteUrl;
			}
			set
			{
				if (this.websiteUrl != value)
				{
					this.websiteUrl = value;
					OnPropertyChanged("WebsiteUrl");
				}
			}
		}
		private string websiteUrl;

		public string Skype
		{
			get
			{
				return this.skype;
			}
			set
			{
				if (this.skype != value)
				{
					this.skype = value;
					OnPropertyChanged("Skype");
				}
			}
		}
		private string skype;

		public string Note
		{
			get
			{
				return this.note;
			}
			set
			{
				if (this.note != value)
				{
					this.note = value;
					OnPropertyChanged("Note");
				}
			}
		}
		private string note;

		public double? Version
		{
			get
			{
				return this.version;
			}
			set
			{
				if (this.version != value)
				{
					this.version = value;
					OnPropertyChanged("Version");
				}
			}
		}
		private double? version = 2.1;

		public string FullName
		{
			get
			{
				return string.Format("{0} {1}", this.FirstName, this.LastName).Trim();
			}
		}

		public string DisplayName
		{
			get
			{
				return this.FullName;
			}
		}

		public string FullAddress
		{
			get
			{
				string fullAddress = string.Empty;
				bool hasBefore = false;
				if (!string.IsNullOrEmpty(this.Address))
				{
					fullAddress = this.Address;
					hasBefore = true;
				}
				if (!string.IsNullOrEmpty(this.City))
				{
					if (hasBefore)
					{
						fullAddress += ", " + this.City;
					}
					else
					{
						fullAddress = this.City;
						hasBefore = true;
					}
				}
				if (!string.IsNullOrEmpty(this.ZipCode))
				{
					if (hasBefore)
					{
						fullAddress += ", " + this.ZipCode;
					}
					else
					{
						fullAddress = this.ZipCode;
						hasBefore = true;
					}
				}
				if (!string.IsNullOrEmpty(this.State))
				{
					if (hasBefore)
					{
						fullAddress += ", " + this.State;
					}
					else
					{
						fullAddress = this.State;
						hasBefore = true;
					}
				}
				if (!string.IsNullOrEmpty(this.Country))
				{
					if (hasBefore)
					{
						fullAddress += ", " + this.Country;
					}
					else
					{
						fullAddress = this.Country;
						hasBefore = true;
					}
				}
				return fullAddress;
			}
		}

		public string Geo
		{
			get
			{
				return this.geo;
			}
			set
			{
				if (this.geo != value)
				{
					this.geo = value;
					OnPropertyChanged("Geo");
				}
			}
		}
		private string geo;

		public string TimeZone
		{
			get
			{
				return this.timeZone;
			}
			set
			{
				if (this.timeZone != value)
				{
					this.timeZone = value;
					OnPropertyChanged("TimeZone");
				}
			}
		}
		private string timeZone;

		public DateTime? Revision
		{
			get
			{
				return this.revision;
			}
			set
			{
				if (this.revision != value)
				{
					this.revision = value;
					OnPropertyChanged("Revision");
				}
			}
		}
		private DateTime? revision;
		#endregion

		#region Public methods
		public override void Parse(string content)
		{
			if (content == null)
			{
				throw new Exception("Content is missing!");
			}

			if (!Regex.IsMatch(content, "BEGIN:VCARD(.*)END:VCARD", RegexOptions.IgnoreCase | RegexOptions.Singleline))
			{
				throw new Exception("Missing 'BEGIN' and 'END' VCARD tags!");
			}

			//Reference: http://en.wikipedia.org/wiki/VCard
			var matches = Regex.Matches(content, @"((?<key>[\w\d;]+):(?<value>[^\n]+)\n)");
			foreach (Match match in matches)
			{
				string key = match.Groups["key"].Value.Trim().ToUpper();
				string value = match.Groups["value"].Value.Trim();

				if (key == "N" && value.Contains(";"))
				{
					string[] parts = value.Split(';');
					this.LastName = parts[0];
					this.FirstName = parts[1];
				}
				else if (key == "FN" && string.IsNullOrEmpty(this.FirstName))
				{
					this.FirstName = value;
				}
				else if (key.StartsWith("ORG"))
				{
					this.CompanyName = value;
				}
				else if (key == "BDAY")
				{
					var m = Regex.Match(value, @"(?<year>\d{4})(?<month>\d{2})(?<day>\d{2})", RegexOptions.IgnoreCase);
					if (m.Success)
					{
						int year = int.Parse(m.Groups["year"].Value);
						int month = int.Parse(m.Groups["month"].Value);
						int day = int.Parse(m.Groups["day"].Value);
						DateTime birthday = DateTime.Now;
						if (DateTime.TryParse(string.Format("{0}-{1}-{2} 00:00:00", year, month, day), out birthday))
						{
							this.Birthday = birthday;
						}
					}
				}
				else if (key.StartsWith("TEL"))
				{
					if (key.ToUpper().Contains("CELL"))
					{
						//this.MobilePhoneNumber = value;
					}
					else if (key.ToUpper().Contains("FAX"))
					{
						//this.FaxNumber = value;
					}
					else if (key.ToUpper().Contains("HOME"))
					{
						//this.HomePhoneNumber = value;
					}
					else
					{
						this.PhoneNumber = value;
					}
				}
				else if (key == "TITLE")
				{
					this.JobTitle = value;
				}
				else if (key.StartsWith("ADR"))
				{
					string[] parts = value.Split(';');
					if (parts.Length >= 6)
					{
						this.Address = parts[2];
						this.City = parts[3];
						this.State = parts[4];
						this.ZipCode = parts[5];
						this.Country = parts[6];
					}
					else
					{
						this.Address = value;
					}
				}
				else if (key.StartsWith("EMAIL"))
				{
					this.EmailAddress = value;
				}
				else if (key.StartsWith("URL"))
				{
					this.WebsiteUrl = value;
				}
				else if (key == "VERSION")
				{
					double v = double.NaN;
					if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out v))
					{
						this.Version = v;
					}
				}
				else if (key.StartsWith("NOTE"))
				{
					this.Note = ContentFormatHelper.DecodeNewLine(value);
				}
			}
		}

		public override string Build()
		{
			var builder = new StringBuilder();
			builder.AppendLine("BEGIN:VCARD");

			//Build version string
			string vs = (this.Version != null ? this.Version.Value.ToString("N1", CultureInfo.InvariantCulture) : "2.1" /* Default */);
			switch (vs)
			{
				//Supported versions
				case "2.1":
				case "3.0":
				case "4.0":
					break;

				default:
					throw new NotSupportedException("VCard version is not supported: " + vs);
			}

			builder.AppendLine("VERSION:" + vs);
			builder.Append("N:").Append(this.LastName).Append(";").Append(this.FirstName).Append(vs == "4.0" ? ";;;" : "").AppendLine();
			builder.Append("FN:").Append(this.FirstName).Append(" ").Append(this.LastName).AppendLine();

			if (!string.IsNullOrEmpty(this.CompanyName))
			{
				builder.Append("ORG:").Append(this.CompanyName).AppendLine();
			}
			if (!string.IsNullOrEmpty(this.JobTitle))
			{
				builder.Append("TITLE:").Append(this.JobTitle).AppendLine();
			}
			if (!string.IsNullOrEmpty(this.Role))
			{
				builder.Append("ROLE:").Append(this.Role).AppendLine();
			}

			switch (vs)
			{
				case "2.1":
					if (!string.IsNullOrEmpty(this.PhoneNumber))
					{
						builder.Append("TEL;WORK;VOICE:").Append(this.PhoneNumber).AppendLine();
					}
					if (!string.IsNullOrEmpty(this.MobileNumber))
					{
						builder.Append("TEL;MOBILE;VOICE:").Append(this.MobileNumber).AppendLine();
						//builder.Append("TEL;CELL;VOICE:").Append(this.MobileNumber).AppendLine();
					}
					if (!string.IsNullOrEmpty(this.Address) || !string.IsNullOrEmpty(this.City) || !string.IsNullOrEmpty(this.ZipCode) || !string.IsNullOrEmpty(this.State) || !string.IsNullOrEmpty(this.Country))
					{
						builder.Append("ADR;WORK:").Append(";;").Append(this.Address).Append(";").Append(this.City).Append(";;").Append(this.ZipCode).Append(";").Append(this.Country).AppendLine();
					}
					break;

				case "3.0":
					if (!string.IsNullOrEmpty(this.PhoneNumber))
					{
						builder.Append("TEL;TYPE=WORK,VOICE:").Append(this.PhoneNumber).AppendLine();
					}
					if (!string.IsNullOrEmpty(this.MobileNumber))
					{
						builder.Append("TEL;TYPE=MOBILE,VOICE:").Append(this.MobileNumber).AppendLine();
						//builder.Append("TEL;TYPE=CELL,VOICE:").Append(this.MobileNumber).AppendLine();
					}
					if (!string.IsNullOrEmpty(this.Address) || !string.IsNullOrEmpty(this.City) || !string.IsNullOrEmpty(this.ZipCode) || !string.IsNullOrEmpty(this.State) || !string.IsNullOrEmpty(this.Country))
					{
						builder.Append("ADR;TYPE=WORK:").Append(";;").Append(this.Address).Append(";").Append(this.City).Append(";;").Append(this.ZipCode).Append(";").Append(this.Country).AppendLine();
					}
					break;

				case "4.0":
					if (!string.IsNullOrEmpty(this.PhoneNumber))
					{
						builder.Append("TEL;TYPE=\"work,voice\";VALUE=uri:tel:").Append(this.PhoneNumber).AppendLine();
					}
					if (!string.IsNullOrEmpty(this.MobileNumber))
					{
						builder.Append("TEL;TYPE=\"mobile,voice\";VALUE=uri:tel:").Append(this.MobileNumber).AppendLine();
						//builder.Append("TEL;TYPE=\"cell,voice\";VALUE=uri:tel:").Append(this.MobileNumber).AppendLine();
					}
					if (!string.IsNullOrEmpty(this.Address) || !string.IsNullOrEmpty(this.City) || !string.IsNullOrEmpty(this.ZipCode) || !string.IsNullOrEmpty(this.State) || !string.IsNullOrEmpty(this.Country))
					{
						builder.Append("ADR;TYPE=\"work\":").Append(";;").Append(this.Address).Append(";").Append(this.City).Append(";;").Append(this.ZipCode).Append(";").Append(this.Country).AppendLine();
					}
					break;
			}

			if (!string.IsNullOrEmpty(this.EmailAddress))
			{
				builder.Append("EMAIL;PREF;INTERNET:").Append(this.EmailAddress).AppendLine();
			}
			if (!string.IsNullOrEmpty(this.Skype))
			{
				builder.Append("X-SKYPE:").Append(this.Skype).AppendLine();
			}
			if (this.Birthday != null)
			{
				builder.Append("BDAY:").Append(this.Birthday.Value.ToString("yyyyMMdd")).AppendLine();
			}
			if (!string.IsNullOrEmpty(this.WebsiteUrl))
			{
				builder.Append("URL:").Append(this.WebsiteUrl).AppendLine();
			}
			if (!string.IsNullOrEmpty(this.Note))
			{
				builder.Append("NOTE:").Append(ContentFormatHelper.EncodeNewLine(this.Note)).AppendLine();
			}
			builder.Append("REV:").Append(DateTime.UtcNow.ToString("yyyyMMddTHHmmssZ")).AppendLine();
			builder.Append("END:VCARD");
			return builder.ToString();
		}

		public override string GetFriendlyName()
		{
			return this.FullName ?? this.EmailAddress ?? this.PhoneNumber ?? this.WebsiteUrl;
		}
		#endregion
	}
}
